9f2b1f
@@ -1,5 +1,5 @@
 /*
- * Copyright 2010-2011 the original author or authors.
+ * Copyright 2011-2012 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -15,88 +15,176 @@
  */
 package org.springframework.data.mongodb.config;
 
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.Set;
 
-import com.mongodb.Mongo;
 import org.springframework.beans.factory.config.BeanDefinition;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
 import org.springframework.context.annotation.Configuration;
+import org.springframework.core.convert.converter.Converter;
 import org.springframework.core.type.filter.AnnotationTypeFilter;
 import org.springframework.data.annotation.Persistent;
 import org.springframework.data.authentication.UserCredentials;
-import org.springframework.data.mongodb.MongoDbFactory;
 import org.springframework.data.mongodb.core.MongoTemplate;
 import org.springframework.data.mongodb.core.SimpleMongoDbFactory;
+import org.springframework.data.mongodb.core.convert.CustomConversions;
 import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
 import org.springframework.data.mongodb.core.mapping.Document;
 import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
 import org.springframework.util.ClassUtils;
 import org.springframework.util.StringUtils;
 
+import com.mongodb.Mongo;
+
+/**
+ * Base class for Spring Data Mongo configuration using JavaConfig.
+ * 
+ * @author Mark Pollack
+ * @author Oliver Gierke
+ */
 @Configuration
 public abstract class AbstractMongoConfiguration {
 
-	public abstract String getDatabaseName();
+	/**
+	 * Return the name of the database to connect to.
+	 * 
+	 * @return must not be {@literal null}.
+	 */
+	protected abstract String getDatabaseName();
 
+	/**
+	 * Return the {@link Mongo} instance to connect to.
+	 * 
+	 * @return
+	 * @throws Exception
+	 */
 	@Bean
 	public abstract Mongo mongo() throws Exception;
 
+	/**
+	 * Creates a {@link MongoTemplate}.
+	 * 
+	 * @return
+	 * @throws Exception
+	 */
 	@Bean
 	public MongoTemplate mongoTemplate() throws Exception {
 		return new MongoTemplate(mongoDbFactory(), mappingMongoConverter());
 	}
 
+	/**
+	 * Creates a {@link SimpleMongoDbFactory} to be used by the {@link MongoTemplate}. Will use the {@link Mongo} instance
+	 * configured in {@link #mongo()}.
+	 * 
+	 * @see #mongo()
+	 * @see #mongoTemplate()
+	 * @return
+	 * @throws Exception
+	 */
 	@Bean
-	public MongoDbFactory mongoDbFactory() throws Exception {
-		if (getUserCredentials() == null) {
+	public SimpleMongoDbFactory mongoDbFactory() throws Exception {
+
+		UserCredentials creadentials = getUserCredentials();
+
+		if (creadentials == null) {
 			return new SimpleMongoDbFactory(mongo(), getDatabaseName());
 		} else {
-			return new SimpleMongoDbFactory(mongo(), getDatabaseName(), getUserCredentials());
+			return new SimpleMongoDbFactory(mongo(), getDatabaseName(), creadentials);
 		}
 	}
 
-	public String getMappingBasePackage() {
-		return "";
+	/**
+	 * Return the base package to scan for mapped {@link Document}s.
+	 * 
+	 * @return
+	 */
+	protected String getMappingBasePackage() {
+		return null;
 	}
 
-	public UserCredentials getUserCredentials() {
+	/**
+	 * Return {@link UserCredentials} to be used when connecting to the MongoDB instance or {@literal null} if none shall
+	 * be used.
+	 * 
+	 * @return
+	 */
+	protected UserCredentials getUserCredentials() {
 		return null;
 	}
 
+	/**
+	 * Creates a {@link MongoMappingContext} equipped with entity classes scanned from the mapping base package.
+	 * 
+	 * @see #getMappingBasePackage()
+	 * @return
+	 * @throws ClassNotFoundException
+	 */
 	@Bean
-	public MongoMappingContext mongoMappingContext() throws ClassNotFoundException, LinkageError {
+	public MongoMappingContext mongoMappingContext() throws ClassNotFoundException {
+
 		MongoMappingContext mappingContext = new MongoMappingContext();
-		String basePackage = getMappingBasePackage();
-		if (StringUtils.hasText(basePackage)) {
-			ClassPathScanningCandidateComponentProvider componentProvider = new ClassPathScanningCandidateComponentProvider(
-					false);
-			componentProvider.addIncludeFilter(new AnnotationTypeFilter(Document.class));
-			componentProvider.addIncludeFilter(new AnnotationTypeFilter(Persistent.class));
+		mappingContext.setInitialEntitySet(getInitialEntitySet());
+		mappingContext.setSimpleTypeHolder(customConversions().getSimpleTypeHolder());
+		mappingContext.afterPropertiesSet();
 
-			Set<Class<?>> initialEntitySet = new HashSet<Class<?>>();
-			for (BeanDefinition candidate : componentProvider.findCandidateComponents(basePackage)) {
-				initialEntitySet.add(ClassUtils.forName(candidate.getBeanClassName(), mappingContext.getClass()
-						.getClassLoader()));
-			}
-			mappingContext.setInitialEntitySet(initialEntitySet);
-		}
 		return mappingContext;
 	}
 
+	/**
+	 * Register custom {@link Converter}s in a {@link CustomConversions} object if required. These
+	 * {@link CustomConversions} will be registered with the {@link #mappingMongoConverter()} and
+	 * {@link #mongoMappingContext()}. Returns an empty {@link CustomConversions} instance by default.
+	 * 
+	 * @return must not be {@literal null}.
+	 */
+	@Bean
+	public CustomConversions customConversions() {
+		return new CustomConversions(Collections.emptyList());
+	}
+
+	/**
+	 * Creates a {@link MappingMongoConverter} using the configured {@link #mongoDbFactory()} and
+	 * {@link #mongoMappingContext()}. Will get {@link #customConversions()} applied.
+	 * 
+	 * @see #customConversions()
+	 * @see #mongoMappingContext()
+	 * @see #mongoDbFactory()
+	 * @return
+	 * @throws Exception
+	 */
 	@Bean
 	public MappingMongoConverter mappingMongoConverter() throws Exception {
 		MappingMongoConverter converter = new MappingMongoConverter(mongoDbFactory(), mongoMappingContext());
-		afterMappingMongoConverterCreation(converter);
+		converter.setCustomConversions(customConversions());
 		return converter;
 	}
 
 	/**
-	 * Hook that allows post-processing after the MappingMongoConverter has been successfully created.
+	 * Scans the mapping base package for classes annotated with {@link Document}.
 	 * 
-	 * @param converter
+	 * @see #getMappingBasePackage()
+	 * @return
+	 * @throws ClassNotFoundException
 	 */
-	protected void afterMappingMongoConverterCreation(MappingMongoConverter converter) {
+	protected Set<Class<?>> getInitialEntitySet() throws ClassNotFoundException {
+
+		String basePackage = getMappingBasePackage();
+		Set<Class<?>> initialEntitySet = new HashSet<Class<?>>();
+
+		if (StringUtils.hasText(basePackage)) {
+			ClassPathScanningCandidateComponentProvider componentProvider = new ClassPathScanningCandidateComponentProvider(
+					false);
+			componentProvider.addIncludeFilter(new AnnotationTypeFilter(Document.class));
+			componentProvider.addIncludeFilter(new AnnotationTypeFilter(Persistent.class));
+
+			for (BeanDefinition candidate : componentProvider.findCandidateComponents(basePackage)) {
+				initialEntitySet.add(ClassUtils.forName(candidate.getBeanClassName(),
+						AbstractMongoConfiguration.class.getClassLoader()));
+			}
+		}
+
+		return initialEntitySet;
 	}
 }
